package com.ec.auth.aspectj;

import java.util.Objects;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.ec.common.annotation.DataSource;
import com.ec.common.utils.StringUtils;
import com.ec.auth.datasource.DynamicDataSourceContextHolder;

/**
 * @SpringBootApplication(scanBasePackages = "com", exclude = {DataSourceAutoConfiguration.class})
 * 这个springBoot注解什么意思 它的主要作用是启用自动配置和组件扫描
 * scanBasePackages = "com": 指定了组件扫描的基本包路径。
 * 这意味着 Spring Boot 将在 com 包及其子包中寻找和自动配置相关的组件（例如控制器、服务、 Repository 等）。
 *
 * exclude = {DataSourceAutoConfiguration.class}: 排除了特定的自动配置类。
 * 在这里，DataSourceAutoConfiguration.class 被排除了。
 * 这意味着 Spring Boot 的数据源自动配置将不会被应用
 *
 * 这里主要是主从数据库的切换
 * 核心原理 多个数据源公用一个回话工厂
 * 根据条件 动态选取数据源进行连接不同库
 * 然后进行sql操作
 * @author ec
 */
@Aspect//@Aspect注解标记为一个切面(aspect本来是方面的意思)。
@Order(1)//@Order(1)注解指定了切面的执行顺序，值为 1 表示这个切面会在其他切面之前执
@Component// @Component注解将这个类标记为 Spring 管理的组件
public class DataSourceAspect {
    protected Logger logger = LoggerFactory.getLogger(getClass());


    // 注解为一个切点，它使用了@Pointcut注解。
    // 切点的定义使用了使用了@annotation或@within来匹配带有com.ec.common.annotation.DataSource注解的方法或类。
    @Pointcut("@annotation(com.ec.common.annotation.DataSource)"
            + "|| @within(com.ec.common.annotation.DataSource)")
    public void dsPointCut() {

    }

    // @Around注解标记为环绕通知，它会在切点匹配的方法执行前后进行操作
    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 首先通过getDataSource(point)获取要切换的数据源
        DataSource dataSource = getDataSource(point);

        // 如果获取到了有效的数据源，将其设置到DynamicDataSourceContextHolder中
        if (StringUtils.isNotNull(dataSource)) {
            DynamicDataSourceContextHolder.setDataSourceKey(dataSource.value().name());
        }

        // 如果获取到了有效的数据源，将其设置到DynamicDataSourceContextHolder
        try {
            return point.proceed();
        } finally {
            // 销毁数据源 在执行方法之后
            DynamicDataSourceContextHolder.clearDataSourceKey();
        }
    }

    /**
     * 获取需要切换的数据源
     */
    public DataSource getDataSource(ProceedingJoinPoint point) {
        // 它通过AnnotationUtils.findAnnotation方法在方法或类上查找DataSource注解，并返回对应的数据源对象

        MethodSignature signature = (MethodSignature) point.getSignature();
        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        if (Objects.nonNull(dataSource)) {
            return dataSource;
        }

        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
    }
}
